﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AForge.Video.DirectShow;

using Graphene.Processing;
using Graphene.Storing;

namespace Graphene
{    
    public delegate void SetImageInPictureboxDelegate(System.Drawing.Bitmap bitmap);
    public delegate void SetVideoSourceSelectionDelegate(FilterInfo filterInfo);

    public class StereoProcessingController
    {
        public StereoProcessingController(Model mo, MainForm vi)
        {
            this.model = mo;
            this.view = vi;

            this.view.RefreshVideoListButtonFor3DProcessing_SetClickedHandler(this.RefreshVideoListsFor3DProcessing);
            this.view.VideoListPrimaryComboboxFor3DProcessing_SetNewSelectionHandler(this.VideoListPrimaryFor3DProcessing_NewSelection);
            this.view.VideoListSecondaryComboboxFor3DProcessing_SetNewSelectionHandler(this.VideoListSecondaryFor3DProcessing_NewSelection);
            this.view.StartStopCheckboxFor3DProcessing_SetCheckChangedHandler(this.StartStopCheckboxFor3DProcessing_CheckChanged);

            this.RefreshVideoListsFor3DProcessing(this, EventArgs.Empty);
        }

        private void RefreshVideoListsFor3DProcessing(object sender, EventArgs args)
        {
            this.model.RefreshAvailableVideoDeviceList();
            this.view.UpdateVideoListsFor3DProcessing();
        }

        private void VideoListGeneralFor3DProcessing_NewSelection(
            ComboBox comboBox,
            SetVideoSourceSelectionDelegate setVideoSourceSelectionCall          
            )
        {
            if (comboBox.SelectedItem != null
                && !comboBox.Equals(MainForm.SELECTION_NONE))
            {
                string name = (string)comboBox.SelectedItem;
                FilterInfo filterInfo = this.model.SearchVideoDeviceByName(name);
                setVideoSourceSelectionCall(filterInfo);
            }
            else
            {
                setVideoSourceSelectionCall(null);
            }
            this.view.UpdateVideoListsFor3DProcessing();
        }

        private void VideoListPrimaryFor3DProcessing_NewSelection(object sender, EventArgs args)
        {
            VideoListGeneralFor3DProcessing_NewSelection(
                this.view.VideoListPrimaryComboboxFor3DProcessing,
                this.model.SetVideoSourceSelectionPrimaryFor3DProcessing
                );
        }

        private void VideoListSecondaryFor3DProcessing_NewSelection(object sender, EventArgs args)
        {
            VideoListGeneralFor3DProcessing_NewSelection(
                this.view.VideoListSecondaryComboboxFor3DProcessing,
                this.model.SetVideoSourceSelectionSecondaryFor3DProcessing
                );
        }

        private void StartStopCheckboxFor3DProcessing_CheckChanged(object sender, EventArgs args)
        {
            CheckBox checkbox = this.view.StartStopCheckboxFor3DProcessing;
            if (checkbox.CheckState.Equals(CheckState.Checked))
            {
                this.view.VideoListPrimaryComboboxFor3DProcessing.Enabled = false;
                this.view.VideoListSecondaryComboboxFor3DProcessing.Enabled = false;
                this.view.RefreshVideoListButtonFor3DProcessing.Enabled = false;
                AForge.Video.DirectShow.FilterInfo select1 = this.model.GetVideoSourceSelectionPrimaryFor3DProcessing();
                AForge.Video.DirectShow.FilterInfo select2 = this.model.GetVideoSourceSelectionSecondaryFor3DProcessing();
                if (select1 == null || select2 == null)
                {
                    return;
                }
                string idPrimaryVideoSource = this.model.InitVideoSource(
                    select1.MonikerString,
                    "VideoSource-" + Model.GenerateId(),
                    select1.Name
                    );
                string idSecondaryVideoSource = this.model.InitVideoSource(
                    select2.MonikerString,
                    "VideoSource-" + Model.GenerateId(),
                    select2.Name
                    );
                model.AddResultChangedHandlerToVideoSource(idPrimaryVideoSource, this.VideoSourcePrimary_NewFrame);
                model.AddResultChangedHandlerToVideoSource(idSecondaryVideoSource, this.VideoSourceSecondary_NewFrame);
                string idPrimaryImageProcessor = this.model.InitImageProcessor(
                    idPrimaryVideoSource,
                    "MultiColorImageProcessor-" + Model.GenerateId(),
                    "MultiColorImageProcessor-" + select1.Name
                    );
                string idSecondaryImageProcessor = this.model.InitImageProcessor(
                    idSecondaryVideoSource,
                    "MultiColorImageProcessor-" + Model.GenerateId(),
                    "MultiColorImageProcessor-" + select2.Name
                    );
                
                /**
                 * Connecting the ImageProcessor with the Controller. From now on if the ImageProcessor states that its
                 * markers have changed, Controller will be notified in the method specified in the second argument.
                 */
                this.model.AddResultChangedHandlerToImageProcessor(idPrimaryImageProcessor, this.ImageProcessorPrimary_ResultChanged);
                this.model.AddResultChangedHandlerToImageProcessor(idSecondaryImageProcessor, this.ImageProcessorSecondary_ResultChanged);

                /**
                 * Connecting the ImageProcessor with the Controller. From now on if the ImageProcessor finishes processing
                 * an image, Controller will be notified at that method in the second argument.
                 */
                this.model.AddProcessedHandlerToImageProcessor(idPrimaryImageProcessor, this.ImageProcessorPrimary_Processed);
                this.model.AddProcessedHandlerToImageProcessor(idSecondaryImageProcessor, this.ImageProcessorSecondary_Processed);

                this.model.StartVideoSource(idPrimaryVideoSource);
                this.model.StartVideoSource(idSecondaryVideoSource);
                this.model.StartImageProcessor(idPrimaryImageProcessor);
                this.model.StartImageProcessor(idSecondaryImageProcessor);                
                this.view.StartStopCheckboxFor3DProcessing.Text = MainForm.RUNNING_TEXT;
            }
            else if (((CheckBox)this.view.MouselookCheckbox).CheckState.Equals(CheckState.Unchecked))
            {
                AForge.Video.DirectShow.FilterInfo select1 = this.model.GetVideoSourceSelectionPrimaryFor3DProcessing();
                AForge.Video.DirectShow.FilterInfo select2 = this.model.GetVideoSourceSelectionSecondaryFor3DProcessing();

                string idPrimaryVideoSource = this.model.SearchVideoSourceIdByName(select1.Name);
                string idSecondaryVideoSource = this.model.SearchVideoSourceIdByName(select2.Name);
                string idPrimaryImageProcessor = this.model.SearchImageProcessorIdByName("MultiColorImageProcessor-" + select1.Name);
                string idSecondaryImageProcessor = this.model.SearchImageProcessorIdByName("MultiColorImageProcessor-" + select2.Name);

                if (idPrimaryVideoSource == null || idSecondaryVideoSource == null
                    || idPrimaryImageProcessor == null || idSecondaryImageProcessor == null)
                {
                    //a bit brutal, but for now, let it be enough.
                    return;
                }

                this.model.StopImageProcessor(idPrimaryImageProcessor);
                this.model.StopImageProcessor(idSecondaryImageProcessor);
                this.model.StopVideoSource(idPrimaryVideoSource);
                this.model.StopVideoSource(idSecondaryVideoSource);
                
                this.model.RemoveResultChangedHandlerToImageProcessor(idPrimaryImageProcessor, this.ImageProcessorPrimary_ResultChanged);
                this.model.RemoveResultChangedHandlerToImageProcessor(idSecondaryImageProcessor, this.ImageProcessorSecondary_ResultChanged);
                this.model.RemoveProcessedHandlerToImageProcessor(idPrimaryImageProcessor, this.ImageProcessorPrimary_Processed);
                this.model.RemoveProcessedHandlerToImageProcessor(idSecondaryImageProcessor, this.ImageProcessorSecondary_Processed);
                this.model.RemoveResultChangedHandlerToVideoSource(idPrimaryVideoSource, this.VideoSourcePrimary_NewFrame);
                this.model.RemoveResultChangedHandlerToVideoSource(idSecondaryVideoSource, this.VideoSourceSecondary_NewFrame);

                this.model.CloseImageProcessor(idPrimaryImageProcessor);
                this.model.CloseImageProcessor(idSecondaryImageProcessor);
                this.model.CloseVideoSource(idPrimaryVideoSource);
                this.model.CloseVideoSource(idSecondaryVideoSource);

                this.view.SetImageInProcessedPrimaryPictureboxFor3DProcessing(global::Graphene.Properties.Resources.o_small_02);
                this.view.SetImageInVideoPrimaryPictureboxFor3DProcessing(global::Graphene.Properties.Resources.o_small_02);
                this.view.SetImageInVideoSecondaryPictureboxFor3DProcessing(global::Graphene.Properties.Resources.o_small_02);
                this.view.SetImageInProcessedPrimaryPictureboxFor3DProcessing(global::Graphene.Properties.Resources.o_small_02);
                this.view.SetImageInProcessedSecondaryPictureboxFor3DProcessing(global::Graphene.Properties.Resources.o_small_02);                
                this.view.StartStopCheckboxFor3DProcessing.Text = MainForm.STOPPED_TEXT;                
                this.view.VideoListPrimaryComboboxFor3DProcessing.Enabled = true;
                this.view.VideoListSecondaryComboboxFor3DProcessing.Enabled = true;
                this.view.RefreshVideoListButtonFor3DProcessing.Enabled = true;
            }
        }

        private void VideoSourceGeneral_NewFrame(
            object sender,
            PictureBox pictureBox,
            SetImageInPictureboxDelegate setImageCall
            )
        {
            VideoSource senderVideoSource = (VideoSource)sender;
            if ((object)senderVideoSource == null)
            {
                return;
            }

            string deviceId = senderVideoSource.MyId;
            byte[] byteImage = (byte[])senderVideoSource.ResultData;
            System.Drawing.Bitmap bitmap = senderVideoSource.Bitmap;

            if (bitmap != null && byteImage != null)
            {
                byte[] resizedByteImage = AbstractProcessor.Resize(
                    byteImage,
                    bitmap.Width,
                    bitmap.Height,
                    pictureBox.Size.Width,
                    pictureBox.Size.Height
                    );
                System.Drawing.Bitmap resizedBitmap = AbstractProcessor.ByteImageToBitmap_Unsafe(
                    resizedByteImage,
                    pictureBox.Size.Width,
                    pictureBox.Size.Height
                    );
                setImageCall(resizedBitmap);
            }
            else
            {
                setImageCall(global::Graphene.Properties.Resources.o_small_02);
            }
        }

        private void VideoSourcePrimary_NewFrame(object sender, object whatChanged)
        {
            VideoSourceGeneral_NewFrame(
                sender,
                view.VideoPrimaryPictureboxFor3DProcessing,
                this.view.SetImageInVideoPrimaryPictureboxFor3DProcessing
                );
        }

        private void VideoSourceSecondary_NewFrame(object sender, object whatChanged)
        {
            VideoSourceGeneral_NewFrame(
                sender,
                view.VideoSecondaryPictureboxFor3DProcessing,
                this.view.SetImageInVideoSecondaryPictureboxFor3DProcessing
                );            
        }

        private void ImageProcessorPrimary_ResultChanged(object sender, object whatChanged)
        {

        }
        private void ImageProcessorSecondary_ResultChanged(object sender, object whatChanged)
        {

        }

        private void ImageProcessorGeneral_Processed(
            object sender,
            PictureBox drawnPictureBox,
            SetImageInPictureboxDelegate setImageCall
            )
        {
            MultiColorImageProcessor senderImageProcessor = (MultiColorImageProcessor)sender;
            if ((object)senderImageProcessor == null)
            {
                return;
            }

            byte[] byteImage = (byte[])senderImageProcessor.CopiedSourceData;
            Dictionary<string, PointInt2D> firstPoints = senderImageProcessor.FirstPixelsFound;
            Dictionary<string, Spat> spats = senderImageProcessor.Spats;
            Dictionary<string, MarkerInImage> markers = (Dictionary<string, MarkerInImage>)senderImageProcessor.ResultData;

            byte[] modifiedByteImage = new byte[senderImageProcessor.CopiedImageSize.X * senderImageProcessor.CopiedImageSize.Y * 3];
            byteImage.CopyTo(modifiedByteImage, 0);

            /**
             * Draw spats.
             */
            /*
            for (int i = 0; i < markers.Count; i++)
            {
                if (markers[i].Exists)
                {
                    modifiedByteImage = ImageProcessor.SpatOverByteImage(spats[i], modifiedByteImage, senderImageProcessor.ImageSize.X, senderImageProcessor.ImageSize.Y, Color.LightGray, 0.9);                        
                }
            }    
            */

            /**
             * Resize.
             */
            modifiedByteImage = AbstractProcessor.ResizeAndFadeToColor(modifiedByteImage, senderImageProcessor.CopiedImageSize.X, senderImageProcessor.CopiedImageSize.Y, drawnPictureBox.Size.Width, drawnPictureBox.Size.Height, System.Drawing.Color.FromArgb(0, 0, 0), 0.4);

            /**
             * Draw first points and markers (Xs).
             */
            foreach (MarkerInImage localMarker in markers.Values)
            {
                if (localMarker.Exists)
                {
                    //modifiedByteImage = ImageProcessor.DrawX(modifiedByteImage, MainForm.VIEWFINDER_WIDTH, MainForm.VIEWFINDER_HEIGHT, new Point2D((int)(firstPoints[i].X / ((double)senderImageProcessor.ImageSize.X / MainForm.VIEWFINDER_WIDTH)), (int)(firstPoints[i].Y / ((double)senderImageProcessor.ImageSize.Y / MainForm.VIEWFINDER_HEIGHT))), Color.Gray); outdated coordinates

                    modifiedByteImage = AbstractProcessor.DrawX(
                        modifiedByteImage,
                        drawnPictureBox.Size.Width,
                        drawnPictureBox.Size.Height,
                        new PointInt2D(
                            (int)(localMarker.Position.X * drawnPictureBox.Size.Width / 2 + drawnPictureBox.Size.Width / 2),
                            (int)(localMarker.Position.Y * drawnPictureBox.Size.Height / 2 + drawnPictureBox.Size.Height / 2)
                        ),
                        localMarker.RepresentingColor
                    );
                }
            }
            System.Drawing.Bitmap shownBitmap = AbstractProcessor.ByteImageToBitmap_Unsafe(modifiedByteImage, drawnPictureBox.Size.Width, drawnPictureBox.Size.Height);            
            setImageCall(shownBitmap);
        }

        private void ImageProcessorPrimary_Processed(object sender)
        {
            ImageProcessorGeneral_Processed(
                sender,
                this.view.ProcessedPrimaryPictureboxFor3DProcessing,
                this.view.SetImageInProcessedPrimaryPictureboxFor3DProcessing
                );
        }

        private void ImageProcessorSecondary_Processed(object sender)
        {
            ImageProcessorGeneral_Processed(
                sender,
                this.view.ProcessedSecondaryPictureboxFor3DProcessing,
                this.view.SetImageInProcessedSecondaryPictureboxFor3DProcessing
                );
        }

        private Model model;
        private MainForm view;
    }
}
